﻿namespace Microsoft.VisualStudio.ServiceModel.DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.Data;
    using System.Data.Mapping;
    using System.Data.Metadata.Edm;
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    using System.Globalization;
    using System.IO;
    using System.ServiceModel.DomainServices.EntityFramework;
    using System.Text;

    internal class LinqToEntitiesContext : LinqToEntitiesContextBase
    {
        private System.Data.Metadata.Edm.MetadataWorkspace _metadataWorkspace;
        private const string EntityStatePropertyName = "EntityState";
        private const string GetEntityStateHelperMethodName = "GetEntityState";

        public LinqToEntitiesContext(Type contextType) : base(contextType)
        {
        }

        protected override CodeTypeDeclaration CreateBusinessLogicClass(CodeGenContext codeGenContext, CodeNamespace codeNamespace, string className)
        {
            foreach (string str in BusinessLogicClassConstants.LinqToEntitiesImports)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(str));
            }
            if (base.ContextType.Namespace != codeNamespace.Name)
            {
                codeNamespace.Imports.Add(new CodeNamespaceImport(base.ContextType.Namespace));
            }
            codeGenContext.AddReference(typeof(ObjectContext).Assembly.FullName);
            codeGenContext.AddReference(typeof(LinqToEntitiesDomainService<>).Assembly.FullName);
            CodeTypeDeclaration declaration = CodeGenUtilities.CreateTypeDeclaration(className, codeNamespace.Name);
            CodeTypeReference reference = new CodeTypeReference(BusinessLogicClassConstants.LinqToEntitiesDomainServiceTypeName, new CodeTypeReference[] { new CodeTypeReference(base.ContextType.Name) });
            declaration.BaseTypes.Add(reference);
            return declaration;
        }

        protected override void GenerateDeleteMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            CodeExpression expression2;
            string name = CodeGenUtilities.MakeLegalParameterName(entity.Name);
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);
            method.Name = "Delete" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, name));
            LinqToEntitiesEntity entity2 = (LinqToEntitiesEntity) entity;
            CodeArgumentReferenceExpression targetObject = new CodeArgumentReferenceExpression(name);
            if (GenerateGetEntityStateIfNecessary(codeGenContext, businessLogicClass, entity))
            {
                expression2 = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "GetEntityState", new CodeExpression[] { targetObject });
            }
            else
            {
                expression2 = new CodePropertyReferenceExpression(targetObject, "EntityState");
            }
            CodeExpression expression3 = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
            CodePropertyReferenceExpression expression4 = new CodePropertyReferenceExpression(expression3, entity2.DefaultObjectSetName);
            CodeFieldReferenceExpression right = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodeExpression condition = CodeGenUtilities.MakeNotEqual(typeof(EntityState), expression2, right, codeGenContext.IsCSharp);
            CodeFieldReferenceExpression expression7 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Deleted));
            CodePropertyReferenceExpression expression8 = new CodePropertyReferenceExpression(expression3, "ObjectStateManager");
            CodeMethodInvokeExpression expression = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(expression8, "ChangeObjectState"), new CodeExpression[] { targetObject, expression7 });
            CodeStatement[] trueStatements = new CodeStatement[] { new CodeExpressionStatement(expression) };
            CodeMethodInvokeExpression expression10 = new CodeMethodInvokeExpression(expression4, "Attach", new CodeExpression[] { targetObject });
            CodeMethodInvokeExpression expression11 = new CodeMethodInvokeExpression(expression4, "DeleteObject", new CodeExpression[] { targetObject });
            CodeStatement[] falseStatements = new CodeStatement[] { new CodeExpressionStatement(expression10), new CodeExpressionStatement(expression11) };
            CodeConditionStatement statement = new CodeConditionStatement(condition, trueStatements, falseStatements);
            method.Statements.Add(statement);
        }

        private static CodeTypeMember GenerateGetEntityState(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass)
        {
            CodeNamespace namespace2 = codeGenContext.GetNamespace(businessLogicClass);
            if (namespace2 != null)
            {
                namespace2.Imports.Add(new CodeNamespaceImport("System.Data.Objects"));
            }
            CodeMemberMethod member = new CodeMemberMethod {
                Name = "GetEntityState",
                ReturnType = new CodeTypeReference(typeof(EntityState).Name),
                Attributes = MemberAttributes.Private
            };
            member.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "entity"));
            member.Statements.Add(new CodeVariableDeclarationStatement("ObjectStateEntry", "stateEntry", new CodePrimitiveExpression(null)));
            CodeArgumentReferenceExpression expression = new CodeArgumentReferenceExpression("entity");
            CodeExpression targetObject = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
            CodeFieldReferenceExpression expression3 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodePropertyReferenceExpression expression4 = new CodePropertyReferenceExpression(targetObject, "ObjectStateManager");
            CodeVariableReferenceExpression expression5 = new CodeVariableReferenceExpression("stateEntry");
            CodeVariableReferenceExpression expression6 = new CodeVariableReferenceExpression("_out_stateEntry");
            CodeMethodInvokeExpression left = new CodeMethodInvokeExpression(expression4, "TryGetObjectStateEntry", new CodeExpression[] { expression, expression6 });
            CodeExpression condition = CodeGenUtilities.MakeEqual(typeof(bool), left, new CodePrimitiveExpression(false), codeGenContext.IsCSharp);
            CodeMethodReturnStatement statement = new CodeMethodReturnStatement(expression3);
            CodeConditionStatement statement2 = new CodeConditionStatement(condition, new CodeStatement[] { statement });
            member.Statements.Add(statement2);
            member.Statements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(expression5, "State")));
            StringBuilder stringBuilder = null;
            CodeDomProvider provider = codeGenContext.Provider;
            using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture))
            {
                provider.GenerateCodeFromMember(member, writer, codeGenContext.CodeGeneratorOptions);
                stringBuilder = writer.GetStringBuilder();
            }
            stringBuilder.Replace("_out_", codeGenContext.IsCSharp ? "out " : string.Empty);
            string indentString = codeGenContext.CodeGeneratorOptions.IndentString;
            indentString = indentString + indentString;
            return new CodeSnippetTypeMember(indentString + stringBuilder.ToString().Replace(Environment.NewLine, Environment.NewLine + indentString).TrimEnd(new char[] { ' ' }));
        }

        private static bool GenerateGetEntityStateIfNecessary(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            if (typeof(EntityObject).IsAssignableFrom(entity.ClrType))
            {
                return false;
            }
            BusinessLogicContext.GenerateHelperMemberIfNecessary(codeGenContext, businessLogicClass, "GetEntityState", () => GenerateGetEntityState(codeGenContext, businessLogicClass));
            return true;
        }

        protected override void GenerateInsertMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            CodeExpression expression3;
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);
            string name = CodeGenUtilities.MakeLegalParameterName(entity.Name);
            LinqToEntitiesEntity entity2 = (LinqToEntitiesEntity) entity;
            method.Name = "Insert" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, name));
            CodeArgumentReferenceExpression targetObject = new CodeArgumentReferenceExpression(name);
            CodeExpression expression2 = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
            if (GenerateGetEntityStateIfNecessary(codeGenContext, businessLogicClass, entity))
            {
                expression3 = new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "GetEntityState", new CodeExpression[] { targetObject });
            }
            else
            {
                expression3 = new CodePropertyReferenceExpression(targetObject, "EntityState");
            }
            CodeFieldReferenceExpression expression4 = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Added));
            CodeFieldReferenceExpression right = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(EntityState).Name), Enum.GetName(typeof(EntityState), EntityState.Detached));
            CodePropertyReferenceExpression expression6 = new CodePropertyReferenceExpression(expression2, "ObjectStateManager");
            CodeMethodInvokeExpression expression = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(expression6, "ChangeObjectState"), new CodeExpression[] { targetObject, expression4 });
            CodeExpression condition = CodeGenUtilities.MakeNotEqual(typeof(EntityState), expression3, right, codeGenContext.IsCSharp);
            CodePropertyReferenceExpression expression9 = new CodePropertyReferenceExpression(expression2, entity2.DefaultObjectSetName);
            CodeMethodInvokeExpression expression10 = new CodeMethodInvokeExpression(expression9, "AddObject", new CodeExpression[] { targetObject });
            CodeConditionStatement statement = new CodeConditionStatement(condition, new CodeStatement[] { new CodeExpressionStatement(expression) }, new CodeStatement[] { new CodeExpressionStatement(expression10) });
            method.Statements.Add(statement);
        }

        protected override CodeMemberMethod GenerateSelectMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            CodeMemberMethod method = null;
            LinqToEntitiesEntity entity2 = entity as LinqToEntitiesEntity;
            if ((entity2 != null) && (entity2.DefaultObjectSetName != null))
            {
                method = new CodeMemberMethod();
                businessLogicClass.Members.Add(method);
                method.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_Query_Method_Remarks, false));
                string text = string.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Query_Method_EF_Remarks, new object[] { entity2.DefaultObjectSetName });
                method.Comments.Add(new CodeCommentStatement(text, false));
                method.Name = "Get" + CodeGenUtilities.MakeLegalEntityName(entity2.DefaultObjectSetName);
                method.ReturnType = new CodeTypeReference("IQueryable", new CodeTypeReference[] { new CodeTypeReference(entity.Name) });
                method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
                CodeExpression targetObject = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
                CodeExpression expression = new CodePropertyReferenceExpression(targetObject, entity2.DefaultObjectSetName);
                CodeMethodReturnStatement statement = new CodeMethodReturnStatement(expression);
                method.Statements.Add(statement);
            }
            return method;
        }

        protected override void GenerateUpdateMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            string name = "current" + entity.ClrType.Name;
            CodeMemberMethod method = new CodeMemberMethod();
            businessLogicClass.Members.Add(method);
            method.Name = "Update" + CodeGenUtilities.MakeLegalEntityName(entity.Name);
            method.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            method.Parameters.Add(new CodeParameterDeclarationExpression(entity.ClrType.Name, name));
            LinqToEntitiesEntity entity2 = (LinqToEntitiesEntity) entity;
            if (!entity2.HasTimestampMember)
            {
                CodeExpression targetObject = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ChangeSet");
                CodeMethodReferenceExpression expression2 = new CodeMethodReferenceExpression(targetObject, "GetOriginal");
                CodeMethodInvokeExpression expression3 = new CodeMethodInvokeExpression(expression2, new CodeExpression[] { new CodeArgumentReferenceExpression(name) });
                CodeExpression expression4 = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
                CodePropertyReferenceExpression expression5 = new CodePropertyReferenceExpression(expression4, entity2.DefaultObjectSetName);
                CodeMethodInvokeExpression expression = new CodeMethodInvokeExpression(expression5, "AttachAsModified", new CodeExpression[] { new CodeArgumentReferenceExpression(name), expression3 });
                CodeExpressionStatement statement = new CodeExpressionStatement(expression);
                method.Statements.Add(statement);
            }
            else
            {
                CodeExpression expression7 = new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), "ObjectContext");
                CodePropertyReferenceExpression expression8 = new CodePropertyReferenceExpression(expression7, entity2.DefaultObjectSetName);
                CodeMethodInvokeExpression expression9 = new CodeMethodInvokeExpression(expression8, "AttachAsModified", new CodeExpression[] { new CodeArgumentReferenceExpression(name) });
                CodeExpressionStatement statement2 = new CodeExpressionStatement(expression9);
                method.Statements.Add(statement2);
            }
        }

        private System.Data.Metadata.Edm.MetadataWorkspace GetMetadataWorkspace()
        {
            System.Data.Metadata.Edm.MetadataWorkspace workspace = System.Data.Mapping.MetadataWorkspaceUtilities.CreateMetadataWorkspace(base.ContextType);
            workspace.LoadFromAssembly(base.ContextType.Assembly);
            return workspace;
        }

        public override System.Data.Metadata.Edm.MetadataWorkspace MetadataWorkspace
        {
            get
            {
                if (this._metadataWorkspace == null)
                {
                    this._metadataWorkspace = this.GetMetadataWorkspace();
                }
                return this._metadataWorkspace;
            }
        }
    }
}

